package demo;

import groovy.lang.Binding;
import groovy.lang.Closure;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.junit.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroovyShellDemo {

    @Test
    public void test1() {
        Binding binding = new Binding();

        // 绑定变量到 groovy 执行环境
        binding.setVariable("foo", new Integer(2));

        // 构建 groovy shell
        GroovyShell shell = new GroovyShell(binding);

        // 执行脚本
        Object value = shell.evaluate("println 'Hello World!'; x = 123; return foo * 10");
        // 执行结果
        String type = value.getClass().getCanonicalName();
        System.out.printf("执行结果：%s, 类型: %s\n", value, value.getClass().getCanonicalName());

        // 获取执行过程中的变量
        Object x = binding.getVariable("x");
        String name = x.getClass().getCanonicalName();
        System.out.printf("执行过程中的x变量：%s, 类型: %s\n", binding.getVariable("x"), binding.getVariable("x").getClass().getCanonicalName());
    }

    @Test
    public void testNan() {
        String scriptText = "x > 2.0";
        Map variable = new HashMap();
        variable.put("x", Double.NaN);

        // 绑定变量到 groovy 执行环境
        Binding binding = new Binding(variable);

        // 构建 groovy shell
        GroovyShell shell = new GroovyShell(binding);

        // 执行脚本
        Object value = shell.evaluate(scriptText);
        // 执行结果
        String type = value.getClass().getCanonicalName();
        System.out.printf("执行结果：%s, 类型: %s\n", value, type);

        // 获取执行过程中的变量
        Object x = binding.getVariable("x");
        String name = x.getClass().getCanonicalName();
        System.out.printf("执行过程中的x变量：%s, 类型: %s\n", x, name);
        System.out.println("Double.NaN > 2.0 = " + (Double.NaN > 2.0));
    }

    @Test
    public void test2() {
        // 绑定变量到 groovy 执行环境
        Binding binding = new Binding();
        binding.setVariable("foo", new Integer(2));

        // 构建 groovy script
        GroovyShell shell = new GroovyShell();
        Script script = shell.parse("println 'Hello World!'; x = 123; return foo * 10");
        script.setBinding(binding);  // 绑定

        // 执行脚本
        Object value = script.run();

        // 执行结果
        System.out.printf("执行结果：%s, 类型: %s\n", value, value.getClass().getCanonicalName());

        // 获取执行过程中的变量
        System.out.printf("执行过程中的x变量：%s, 类型: %s\n", binding.getVariable("x"), binding.getVariable("x").getClass().getCanonicalName());
    }

    /**
     * 使用 InvokerHelper.createScript 创建脚本对象。
     */
    @Test
    public void test3() {
        // 绑定变量到 groovy 执行环境
        Binding binding = new Binding();
        binding.setVariable("foo", new Integer(2));

        GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
        Class scriptClass = groovyClassLoader.parseClass("println 'Hello World!'; x = 123; return foo * 10");

        // 执行脚本
        Object value = InvokerHelper.createScript(scriptClass, binding).run();

        // 执行结果
        System.out.printf("执行结果：%s, 类型: %s\n", value, value.getClass().getCanonicalName());

        // 获取执行过程中的变量
        System.out.printf("执行过程中的x变量：%s, 类型: %s\n", binding.getVariable("x"), binding.getVariable("x").getClass().getCanonicalName());
    }

    /**
     * 在 groovy 脚本中执行 java 定义的函数
     */
    @Test
    public void test4() {
        Binding binding = new Binding();
        binding.setVariable("func", new Func());
        GroovyShell shell = new GroovyShell(binding);
        Object value = shell.evaluate("return func.add(1, 1)");
        System.out.printf("执行结果：%s, 类型：%s\n", value, value.getClass().getCanonicalName());
    }

    /**
     * 在 groovy 脚本中执行 java 定义的静态函数
     */
    @Test
    public void test5() {
        Binding binding = new Binding();
        binding.setVariable("func", Func.class);
        GroovyShell shell = new GroovyShell(binding);
        Object value = shell.evaluate("return func.add2(1, 1)");
        System.out.printf("执行结果：%s, 类型：%s\n", value, value.getClass().getCanonicalName());
    }

    /**
     * Binding 本身也是一种上下文，但有时需要每一个java函数能感知到上下文，此时需要其他的实现方式。
     * <p>
     * 传入上下文的方法有很多，比如作为函数参数，比如作为类的构造函数参数，比如放入 ThreadLocal 。
     */
    @Test
    public void test6() {
        // 构造上下文
        Map<String, Object> context = new HashMap<>();
        context.put("name", "lt");

        Binding binding = new Binding();
        binding.setVariable("func", new Func(context));

        GroovyShell shell = new GroovyShell(binding);

        Object value = shell.evaluate("return func.add(1, 1)");


        System.out.printf("执行结果：%s, 类型：%s\n", value, value.getClass().getCanonicalName());
    }

    /**
     * groovy 有一个闭包的概念，类似 java 中的 lambda 。
     */
    @Test
    public void test7() {
        Binding binding = new Binding();
        binding.setVariable("func", new Func());

        GroovyShell shell = new GroovyShell(binding);

        Object value = shell.evaluate("return func.filter([-1,2,3,4], {x -> x > 0} )");

        System.out.printf("执行结果：%s, 类型：%s\n", value, value.getClass().getCanonicalName());
    }

    /**
     * 在 Java 中定义一个 Groovy 闭包类
     */
    @Test
    public void test8() {
        Binding binding = new Binding();

        binding.setVariable("add", new Add());

        GroovyShell shell = new GroovyShell(binding);

        Object value = shell.evaluate("return add(1,2,3)");

        System.out.printf("执行结果：%s, 类型：%s\n", value, value.getClass().getCanonicalName());
    }

    /**
     * 在 groovy 中修改 java 变量
     */
    @Test
    public void test9() {
        Binding binding = new Binding();

        Person person = new Person();
        person.name = "lt";
        person.age = 10;
        person.books = Arrays.asList("book0", "book1");

        System.out.println("修改前: " + person);

        binding.setVariable("person", person);

        GroovyShell shell = new GroovyShell(binding);

        Object value = shell.evaluate("println 'in groovy books : ' + person.books; person.books[1] = 'new book1';return person.name");

        System.out.printf("执行结果：%s, 类型：%s\n", value, value.getClass().getCanonicalName());

        System.out.println("修改后: " + person);
    }

    public static class Person {
        public String name;
        public int age;
        public List<String> books;

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", books=" + books +
                    '}';
        }
    }

    // 这是一个 groovy 闭包
    public static class Add extends Closure<Long> {

        public Add() {
            super(null, null);
        }

        @Override
        public Long call(Object... args) {
            long result = 0;
            for (Object obj : args) {
                if (obj instanceof Integer) {
                    result = result + (Integer) obj;
                    continue;
                }

                if (obj instanceof Long) {
                    result = result + (Long) obj;
                    continue;
                }
            }
            return result;
        }

    }

    static class Func {

        private Map<String, Object> context;

        // 上下文作为构造函数参数传入
        public Func(Map<String, Object> context) {
            this.context = context;
        }

        public Func() {
        }

        public long add(long a, long b) {
            System.out.println("context = " + context);
            return a + b;
        }

        // 这是一个静态方法
        public static long add2(long a, long b) {
            return a + b;
        }

        // 过滤函数，第2个参数是闭包
        public List<Integer> filter(List<Integer> list, Closure<Boolean> closure) {
            return list.stream().filter(x -> {
                return closure.call(x);
            }).collect(Collectors.toList());
        }

    }


}
